/**
 * \file Sync.h
 * \brief Object oriented synchronization primitives.
 * \author Corey Tabaka
 */

/*
   Copyright 2005 Corey Tabaka

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#ifndef __KERNEL_SYNC_H__
#define __KERNEL_SYNC_H__

#include <spinlock.h>
#include <mutex.h>

// use SMPLOCK() around declaration of lock types used for SMP safety
#if SMP
#define SMPLOCK(type) type
#else
#define SMPLOCK(type) NullLock
#endif

/**
 * \class AutoLock
 * \brief An automatic scope unlock mechanism.
 *
 * This template class accepts a reference to any object supporting lock(void)
 * and unlock(void) methods. The argument object is locked on instantiation and
 * unlocked on destruction. It is meant primarily for use in automatic scope, to
 * ease notation in situations where there are frequent exit paths. There may be
 * some utility for this class as a member variable of short-lived objects,
 * which need to maitain a lock for the duration of their existance.
 */
template <class T>
class AutoLock {
	private:
	
	T &lock;
	
	public:
	
	AutoLock(T &_lock) : lock(_lock) {
		lock.lock();
	}
	
	~AutoLock(void) {
		lock.unlock();
	}
};

/**
 * \class NullLock
 * \brief A non-locking lock class.
 *
 * This class is a substitute for other lock classes, when compiling for
 * uniprocessor systems. Critical sections can be coded to use proper locking
 * and then optimized out by substituting a NullLock for the normal lock class.
 */
class NullLock {
	public:
	
	/**
	 * A dummy lock variable to help maintain some size for locks substituted
	 * with a NullLock.
	 */
	int _lock;
	
	public:
	
	NullLock(void) {}
	~NullLock(void) {}
	
	/**
	 * Initializes the lock to the unlocked state.
	 */
	void inline init(void) {
		_lock = 0;
	}
	
	/**
	 * Attempts to lock the lock. A no-op in this class.
	 */
	void inline lock(void) {}
	
	/**
	 * Attempts to lock the lock without blocking. Always succeeds in this
	 * class.
	 */
	bool inline attempt(void) { return true; }
	
	/**
	 * Unlocks the lock. A no-op in this class.
	 */
	void inline unlock(void) {}
};

/**
 * \class SpinLock
 * \brief An SMP safe spinlock wrapper.
 *
 * This class wraps the spinlock_t lock defined in spinlock.h.
 */
class SpinLock {
	public:
	
	/**
	 * The spinlock that this class wraps.
	 */
	spinlock_t _lock;
	
	public:
	
	/**
	 * Initializes the lock to the unlocked state.
	 */
	SpinLock(void) {
		init();
	}
	~SpinLock(void) {
	}
	
	/**
	 * Initializes the lock to the unlocked state. This inline method will
	 * optimize to a direct call to spinlock_init().
	 */
	void inline init(void) {
		spinlock_init(&_lock);
	}
	
	/**
	 * Attempts to lock the lock or spin if already locked. This inline method
	 * will optimize to a direct call to spinlock_wait().
	 */ 
	void inline lock(void) {
		spinlock_wait(&_lock);
	}
	
	/**
	 * Attempts to lock the lock without spinnign if already locked. This inline
	 * method will optimize to a direct call to spinlock_try().
	 */
	bool inline attempt(void) {
		return spinlock_try(&_lock) == 0;
	}
	
	/**
	 * Unlocks the lock, allowing one of the other threads spinning on this
	 * lock to acquire it. This inline method will optimize to a direct call to
	 * spinlock_signal().
	 */
	void inline unlock(void) {
		spinlock_signal(&_lock);
	}
};

/**
 * \class InterruptLock
 * \brief An SMP safe interrupt lock wrapper.
 *
 * This class wraps the interrupt_mutex_t lock defined in mutex.h.
 */
class InterruptLock {
	public:
	
	/**
	 * The interrupt mutex that this class wraps.
	 */
	int_mutex_t _lock;
	
	public:
	
	/**
	 * Initializes this lock to the unlocked state.
	 */
	InterruptLock(void) {
		init();
	}
	~InterruptLock(void) {
	}
	
	/**
	 * Initializes this lock to an unlocked state.
	 */
	void inline init(void) {
		interrupt_mutex_init(&_lock);
	}
	
	/**
	 * Attempts to lock the lock or spin if already locked. This method will
	 * optimize to a direct call to interrupt_mutex_wait().
	 */
	void inline lock(void) {
		interrupt_mutex_wait(&_lock);
	}
	
	/**
	 * Unlocks the lock, allowing one of the other threads spinning on this lock
	 * to acquire it. This inline method will optimize to a direct call to
	 * interrupt_mutex_signal().
	 */
	void inline unlock(void) {
		interrupt_mutex_signal(&_lock);
	}
};

#if 1
/**
 * \class Mutex
 * \brief An SMP safe mutex lock wrapper.
 *
 * This class wraps the mutex_t lock defined in mutex.h.
 */
class Mutex {
	public:
	
	/**
	 * The mutex this class wraps.
	 */
	mutex_t _lock;
	
	public:
	
	/**
	 * Initializes this lock to the unlocked state.
	 */
	Mutex(void) {
		init();
	}
	~Mutex(void) {
	}
	
	/**
	 * Initializes this lock to the unlocked state.
	 */
	void inline init(void) {
		mutex_init(&_lock);
	}
	
	/**
	 * Attempts to lock the lock, blocking if it is already locked. This inline
	 * method will optimize to a direct call to mutex_wait().
	 */
	void inline lock(void) {
		mutex_wait(&_lock);
	}
	
	/**
	 * Attempts to lock the lock without blocking if it is already locked.
	 * This inline method will optimize to a direct call to mutex_try().
	 */
	bool inline attempt(void) {
		return mutex_try(&_lock) == 0;
	}
	
	/**
	 * Unlocks the lock, notifying the next thread blocked on this lock. This
	 * inline method will optimize to a direct call to mutex_signal().
	 */
	void inline unlock(void) {
		mutex_signal(&_lock);
	}
};

/**
 * \class InterruptLock
 * \brief An SMP safe semaphore lock wrapper.
 *
 * This class wraps the semaphore_t lock defined in mutex.h.
 */
class Semaphore {
	public:
	
	/**
	 * The semaphore this class wraps.
	 */
	semaphore_t _lock;
	
	public:
	
	/**
	 * Initializes this lock to count.
	 */
	Semaphore(int count) {
		init(count);
	}
	~Semaphore(void) {
	}
	
	/**
	 * Initializes this lock to count.
	 */
	void inline init(int count) {
		semaphore_init(&_lock, count);
	}
	
	/**
	 * Attempts to lock this lock, blocking if its count is zero.
	 */
	void inline lock(void) {
		semaphore_wait(&_lock);
	}
	
	/**
	 * Unlocks this lock, incrementing the count and notifying the next thread
	 * blocked on the lock.
	 */
	void inline unlock(void) {
		semaphore_signal(&_lock);
	}
	
	// conventional aliases
	
	/**
	 * This method is an alias for lock(), following the conventional semaphore
	 * operation name.
	 */
	void inline wait(void) {
		semaphore_wait(&_lock);
	}
	
	/**
	 * This mthod is an alias for unlock(), following the conventional semaphore
	 * operation name.
	 */
	void inline signal(void) {
		semaphore_signal(&_lock);
	}
};
#endif

#endif
